﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Red.Toolkit.Profiler.Logic
{
    /// <summary>
    /// A group visible in timeline - a result of merging groups with the same name from multiple data views
    /// </summary>
    public class MergedGroup
    {
        protected string _name;
        private bool _stackStructure;
        private SortedDictionary<int, GroupLevel> _levels; // TODO: do we really need dict of levels?
        private List<Event> _events;

        public MergedGroup( string name, bool stackStructure )
        {
            _name = name;
            _stackStructure = stackStructure;
            _levels = new SortedDictionary<int, GroupLevel>();
            _events = new List<Event>();
        }

        public void Clear()
        {
            _levels.Clear();
            _events.Clear();
        }

        // append events from group
        public void AddGroup( Group group )
        {
            foreach ( var e in group.Events )
                _events.Add( e );
        }

        class EventTime
        {
            public bool start;
            public Event e;

            public EventTime( Event e, bool start)
            {
                this.e = e;
                this.start = start;
            }
        }

        public void Prepare()
        {
            var sortedEvents = new SortedDictionary<double, EventTime>();
            foreach ( var e in _events )
            {
                if ( sortedEvents.ContainsKey( e.Evt.StartTime ) || sortedEvents.ContainsKey( e.Evt.EndTime ) )
                    continue;

                sortedEvents.Add( e.Evt.StartTime, new EventTime( e, true ) );
                sortedEvents.Add( e.Evt.EndTime, new EventTime( e, false ) );
            }

            if ( _stackStructure )
            {
                int depth = 0;
                foreach ( var p in sortedEvents )
                {
                    if ( p.Value.start )
                    {
                        if ( _levels.Count <= depth )
                            _levels.Add( depth, new GroupLevel( depth ) );
                        _levels[depth].events.Add( p.Key, p.Value.e );
                        p.Value.e.Level = depth;
                        depth++;
                    }
                    else
                    {
                        if ( depth > 0 )
                            depth--;
                        else
                            throw new InvalidOperationException();
                    }
                }
            }
            else
            {
                // TODO: temporary
                const int maxLevels = 100;
                bool[] freeLevels = new bool[maxLevels];
                for ( int i = 0; i < maxLevels; ++i )
                    freeLevels[i] = true;

                foreach ( var p in sortedEvents )
                {
                    if ( p.Value.start )
                    {
                        for ( int i = 0; i < maxLevels; ++i )
                            if ( freeLevels[i] )
                            {
                                if ( _levels.Count <= i )
                                    _levels.Add( i, new GroupLevel( i ) );
                                _levels[i].events.Add( p.Key, p.Value.e );
                                p.Value.e.Level = i;
                                freeLevels[i] = false;
                                break;
                            }
                    }
                    else
                    {
                        freeLevels[p.Value.e.Level] = true;
                    }
                }
            }

            int totalEvents = 0;
            foreach ( var l in _levels )
            {
                l.Value.BuildTree();
                totalEvents += l.Value.events.Count;
            }

            Debug.Write( "Total events in group '" + Name + "': " + totalEvents + "\n" );
        }

        public string Name { get { return _name; } }
        public List<Event> Events { get { return _events; } }
        public SortedDictionary<int, GroupLevel> Levels { get { return _levels; } }

        public void UpdateSelection()
        {
            foreach ( var l in _levels )
                l.Value.UpdateSelection();
        }

        public List<Event> SelectSubevents( Event e )
        {
            List<Event> result = new List<Event>();

            // TODO: this could be optimized somehow

            foreach ( var subevent in _events )
            {
                if ( subevent.Evt.StartTime >= e.Evt.StartTime &&
                    subevent.Evt.StartTime <= e.Evt.EndTime )
                {
                    result.Add( subevent );
                    subevent.Style = Event.DrawStyle.Selected;
                }
            }

            UpdateSelection();

            return result;
        }

        public string DetailsString { get { return _levels.Count + " levels"; } }
    }
}
